class Rect {
  constructor(x, y, w, h) {
    this.x = x
    this.y = y
    this.w = w
    this.h = h
  }

  contains(x, y) {
    return (x>=this.x && x<=this.x+this.w && y>=this.y && y<=this.y+this.h)
  }
}

class RegularClock {
  /**
   * Set rendering context.
   * @param {CanvasRenderingContext2D} context
   */
  constructor(context) {
    this.context = context;
  }

  setBox(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    console.log("Clock size changed to", width, height);
  }

  drawFace(ctx, radius) {
    var grad;
    ctx.beginPath();
    ctx.arc(0, 0, radius, 0, 2 * Math.PI);
    ctx.fillStyle = "white";
    ctx.fill();
    grad = ctx.createRadialGradient(0, 0, radius * 0.95, 0, 0, radius * 1.05);
    grad.addColorStop(0, "#333");
    grad.addColorStop(0.5, "white");
    grad.addColorStop(1, "#333");
    ctx.strokeStyle = grad;
    ctx.lineWidth = radius * 0.1;
    ctx.stroke();
    ctx.beginPath();
    ctx.arc(0, 0, radius * 0.1, 0, 2 * Math.PI);
    ctx.fillStyle = "#333";
    ctx.fill();
  }

  drawNumbers(ctx, radius) {
    var ang;
    var num;
    ctx.font = radius * 0.15 + "px arial";
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    for (num = 1; num < 13; num++) {
      ang = (num * Math.PI) / 6;
      ctx.rotate(ang);
      ctx.translate(0, -radius * 0.85);
      ctx.rotate(-ang);
      ctx.fillText(num.toString(), 0, 0);
      ctx.rotate(ang);
      ctx.translate(0, radius * 0.85);
      ctx.rotate(-ang);
    }
  }

  drawTime(ctx, radius) {
    var now = new Date();
    var hour = now.getHours();
    var minute = now.getMinutes();
    var second = now.getSeconds();
    //hour
    hour = hour % 12;
    hour =
      (hour * Math.PI) / 6 +
      (minute * Math.PI) / (6 * 60) +
      (second * Math.PI) / (360 * 60);
    this.drawHand(ctx, hour, radius * 0.5, radius * 0.07);
    //minute
    minute = (minute * Math.PI) / 30 + (second * Math.PI) / (30 * 60);
    this.drawHand(ctx, minute, radius * 0.8, radius * 0.07);
    // second
    second = (second * Math.PI) / 30;
    this.drawHand(ctx, second, radius * 0.9, radius * 0.02);

    // write the timestamp on screen
    ctx.font = radius * 0.15 + "px arial";
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";

    ctx.fillText(now.toLocaleTimeString("en-US", {hour12:false}), 0, radius*0.3)
  }

  drawHand(ctx, pos, length, width) {
    ctx.beginPath();
    ctx.lineWidth = width;
    ctx.lineCap = "round";
    ctx.moveTo(0, 0);
    ctx.rotate(pos);
    ctx.lineTo(0, -length);
    ctx.stroke();
    ctx.rotate(-pos);
  }

  draw() {
    let center_x = this.x + this.width / 2;
    let center_y = this.y + this.height / 2;
    let radius = Math.min(this.width, this.height) / 2 * 0.9;
    let ctx = this.context;

    ctx.save();
    ctx.translate(center_x, center_y);
    this.drawFace(ctx, radius);
    this.drawNumbers(ctx, radius);
    this.drawTime(ctx, radius);
    ctx.restore();
  }
}

class CountDown {
  static idleSound = new Audio("idle.mp3")
  static workSound = new Audio("work.mp3")
  /**
   * Set rendering context.
   * @param {CanvasRenderingContext2D} context
   */
   constructor(context) {
    this.context = context
    this.state = "init" // work or idle
    this.workTime = 30 * 60 // default work time is 30 minutes
    this.idleTime = 10 * 60 // default idle time is 10 minutes
    this.timeLimit = 0
  }

  rect() {
    let center_x = this.x + this.width / 2;
    let center_y = this.y + this.height / 2;
    let radius = Math.min(this.width, this.height) / 2 * 0.9;
    return new Rect(center_x-radius, center_y-radius, radius*2, radius*2)
  }

  centerRect() {
    let center_x = this.x + this.width / 2;
    let center_y = this.y + this.height / 2;
    let radius = Math.min(this.width, this.height) / 2 * 0.1;
    return new Rect(center_x-radius, center_y-radius, radius*2, radius*2)
  }

  setTimeCountDown(seconds) {
    this.timerStart = new Date();
    this.timeLimit = seconds;
    if (this.state == "work") {
      this.workTime = seconds
    } else {
      this.idleTime = seconds
    }
  }

  toggleWorkIdle() {
    if (this.state == "work") {
      this.state = "idle"
      CountDown.idleSound.play()
      this.setTimeCountDown(this.idleTime)
    } else {
      if (this.state == "idle") {
        CountDown.workSound.play()
      }
      this.state = "work"
      this.setTimeCountDown(this.workTime)
    }
  }

  setBox(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
  }

  /**
   * Set rendering context.
   * @param {CanvasRenderingContext2D} ctx
   */
  setStyleForState(ctx) {
    if (this.state == "work") {
      ctx.fillStyle = "#833"
      ctx.strokeStyle = "#833"
    } else {
      ctx.fillStyle = "#383"
      ctx.strokeStyle = "#383"
    }
  }

  drawFace(ctx, radius) {
    var grad;
    ctx.beginPath();
    ctx.arc(0, 0, radius, 0, 2 * Math.PI);
    ctx.fillStyle = "white";
    ctx.fill();
    grad = ctx.createRadialGradient(0, 0, radius * 0.95, 0, 0, radius * 1.05);
    grad.addColorStop(0, "#333");
    grad.addColorStop(0.5, "white");
    grad.addColorStop(1, "#333");
    ctx.strokeStyle = grad;
    ctx.lineWidth = radius * 0.1;
    ctx.stroke();
    ctx.beginPath();
    ctx.arc(0, 0, radius * 0.1, 0, 2 * Math.PI);
    this.setStyleForState(ctx)

    ctx.fill();
  }

  drawNumbers(ctx, radius) {
    var ang;
    var num;
    ctx.fillStyle = "#333";
    ctx.font = radius * 0.15 + "px arial";
    ctx.textBaseline = "middle";
    ctx.textAlign = "center";
    for (num = 0; num < 12; num++) {
      ang = (num * Math.PI) / 6;
      ctx.rotate(ang);
      ctx.translate(0, -radius * 0.85);
      ctx.rotate(-ang);
      let minute = num * 5;
      ctx.fillText(minute.toString(), 0, 0);
      ctx.rotate(ang);
      ctx.translate(0, radius * 0.85);
      ctx.rotate(-ang);
    }
  }

  drawHand(ctx, pos, length, width) {
    ctx.beginPath();
    ctx.lineWidth = width;
    ctx.lineCap = "round";
    this.setStyleForState(ctx)
    ctx.moveTo(0, 0);
    ctx.rotate(pos);
    ctx.lineTo(0, -length);
    ctx.stroke();
    ctx.rotate(-pos);
  }

  drawTimeLeft(ctx, radius) {
    if (this.state == "init") {
      // initial state, ask user to press the center to start
      ctx.fillText("开始计时", 0, radius * 0.3)
      return
    }
    let now = new Date()
    let elapsed = (now.getTime() - this.timerStart.getTime()) / 1000
    let secondsLeft = this.timeLimit - elapsed

    if (secondsLeft <= 0) {
      this.toggleWorkIdle()
      return
    }
    
    let angle = secondsLeft * Math.PI / 1800;
    this.drawHand(ctx, angle, radius * 0.7, radius * 0.05)

    let min = Math.floor(secondsLeft/60)
    let sec = parseInt(secondsLeft) % 60
    let timeStr = min.toString().padStart(2, '0') + ":" + sec.toString().padStart(2, '0')
    ctx.fillText(timeStr, 0, radius * 0.3)
  }

  draw() {
    let center_x = this.x + this.width / 2;
    let center_y = this.y + this.height / 2;
    let radius = Math.min(this.width, this.height) / 2 * 0.9;
    let ctx = this.context;

    ctx.save();
    ctx.translate(center_x, center_y);
    this.drawFace(ctx, radius);
    this.drawNumbers(ctx, radius);
    this.drawTimeLeft(ctx, radius);
    ctx.restore();
  }

  handleClick(x, y) {
    console.log("CountDown", x, y)
    if (this.centerRect().contains(x, y) || (this.state == "init")) {
      console.log("Clicked center rect")
      this.toggleWorkIdle()
      this.draw()
    } else {
      // calculate the angle
      let center_x = this.x + this.width / 2;
      let center_y = this.y + this.height / 2; 
      let delta_x = x-center_x
      let delta_y = y-center_y
      let angle = Math.atan2(delta_x, -delta_y)
      if (angle < 0) {
        angle += Math.PI * 2
      }
      let seconds = 3600 * angle / Math.PI / 2;
      this.setTimeCountDown(seconds)
      console.log("Angle:", angle, seconds)
      this.draw()
    }
  }
}

// Wrapper class for the clock
class CountDownClock {
  /**
   *
   * @param {HTMLCanvasElement} canvas
   */
  constructor(canvas) {
    this.canvas = canvas;
    this.context = this.canvas.getContext("2d");
    this.clock = new RegularClock(this.context);
    this.countDown = new CountDown(this.context);
    this.margin = 30;
  }

  /**
   * Set stage size.
   * @param {number} width
   * @param {number} height
   */
  setSize(width, height) {
    this.width = width;
    this.height = height;
    this.canvas.width = width;
    this.canvas.height = height;

    if (width > height) {
      // horizontal
      this.countDown.setBox(this.margin, this.margin, width /2 - this.margin*2, height - this.margin*2);
      this.clock.setBox(this.margin + width/2, this.margin,
          width/2 - this.margin*2, height - this.margin*2)
    } else {
      // vertical
      this.countDown.setBox(this.margin, this.margin,
        this.width-this.margin*2, this.height/2-this.margin*2)
      this.clock.setBox(this.margin, this.height/2+this.margin,
        this.width-this.margin*2, this.height/2-this.margin*2)
    }

  }

  cleanCanvas() {
    let context = this.context;
    context.fillStyle = `rgba(0, 0, 0, 0.15)`;
    // Fill entire canvas.
    context.fillRect(0, 0, this.width, this.height);
  }

  draw() {
    this.clock.draw();
    this.countDown.draw();
  }

  handleClick(x, y) {
    if (this.countDown.rect().contains(x, y)) {
      this.countDown.handleClick(x, y)
    }
  }
}

let clockCanvas = document.getElementById("clock-canvas");

let countDownClock = new CountDownClock(clockCanvas);
countDownClock.setSize(window.innerWidth, window.innerHeight);

function loop() {
  countDownClock.cleanCanvas();

  countDownClock.draw();
  window.setTimeout(loop, 500);
}

window.onload = loop;

window.onresize = function (e) {
  countDownClock.setSize(window.innerWidth, window.innerHeight);
};
window.onmousedown = function (e) {
  countDownClock.handleClick(e.pageX, e.pageY);
};
window.ontouchstart = function (e) {
  countDownClock.handleClick(e.pageX, e.pageY);
};
